Exploring Global Carbon Budget 2025

Author

Xin Zhao

Motivation

The Global Carbon Budget (GCB) is the flagship report and dataset updated annually to monitor, estimate, and account for carbon emissions. With the release of GCB 2024 in March 2025, we take this opportunity to briefly explore the data from a GCAM modeler’s perspective.

We will focus in particular on net land-use change emissions (ELUC)—an emission category (source) that GCAM can report (namely anthropogenic land use change emissions or LULUCF/AFOLU CO2 emissions), though with substantial uncertainty and potential discrepancies in definition. In this release, GCB introduced a new bookkeeping model, LUCE1, for estimating ELUC, alongside the original three: BLUE, H&C20232, and OSCAR3 (see Table 4 in GCB 2024). For the main ELUC categories—(1) deforestation, (2) forest regrowth, (3) other transitions, and (4) wood harvest & other forest management—GCB uses the average of the four bookkeeping models as its default estimate. However, for the fifth category, (5) peat drainage and peat fires, all models rely on external estimates, specifically the mean of values from FAO, LPX-Bern, and ORCHIDEE.

From a technical perspective, GCAM also incorporates a bookkeeping-style approach for estimating ELUC based on land use change results in GCAM. However, it operates at a coarse spatial resolution and lacks, or has only limited, representation of key processes such as land transitions, forest management, and peatland drainage and fires. Here, we briefly compare ELUC estimates from the GCB bookkeeping models with GCAM results for recent years, focusing on the period around 2021—the new base year for GCAM.

Bookkeeping models (screenshot of Table 4 in GCB 2024)
Diffs in bookkeeping models?

Bookkeeping models can differ significantly in terms of spatial resolution, input data, processes represented, and underlying assumptions (see Section S2.1 in GCB2025). For example, HC2023 uses FAO national-level land-use data, whereas others (OSCAR also has a computational unit at the country level?) mainly rely on LUH2 developed by George Hurtt’s group (UMD). As noted in H&C (2023), LUH2 itself draws on land-use change data from the FAO and version 3.3 of the History Database of the Global Environment (HYDE; maintained by Goldewijk et al., PBL).

GCB 2024 data

  • Load the package and data; check models and data elements.
Code
library(dplyr)
library(tidyr)
library(ggplot2)
library(plotly)
library(kableExtra)

readr::read_csv("../data/GCB/GCB2025_Eluc.csv", comment = "#") ->
  Eluc_world

# make model factor
Eluc_world %>% 
  mutate(model = factor(model,
                 levels = c("BLUE", "HC2023", "OSCAR", "LUCE", "GCB"))) ->
  Eluc_world

Eluc_world %>% filter(year == last(year)) %>% 
  head(n = 5)
# A tibble: 5 x 8
   year model    Net `deforestation (total)` `forest regrowth~` `other transit~`
  <dbl> <fct>  <dbl>                   <dbl>              <dbl>            <dbl>
1  2023 GCB    0.989                    1.66             -1.22            0.0382
2  2023 BLUE   1.12                     1.50             -1.35            0.0791
3  2023 HC2023 0.661                    1.57             -1.07           -0.0472
4  2023 OSCAR  1.10                     2.28             -1.54            0.0612
5  2023 LUCE   1.07                     1.29             -0.916           0.0595
# ... with 2 more variables: `peat drainage & peat fires` <dbl>,
#   `wood harvest & other forest management` <dbl>
Code
Eluc_world %>% mutate(value = Net * 44 / 12) %>% 
  ggplot() +
  geom_line(aes(x = year, y = value, color = model, linetype = model),
            size = 1.1) +
  geom_hline(yintercept = 0) +
  scale_color_brewer(palette = "Set1") +
  scale_linetype_manual(values = c(rep(1, 4), 5)) +
  theme_bw() + 
  labs(x = "Year", y = "GtCO2", color = "Model", linetype = "Model",
       caption = "Data source: GCB 2024",
       title = "GCB: Global Eluc") -> p

ggplotly(p)
Code
Eluc_world %>% mutate(value = Net * 44 / 12) %>% 
  group_by(model) %>% 
  mutate(value = value -lag(value)) %>% 
  ggplot() +
  geom_line(aes(x = year, y = value, color = model, linetype = model),
            size = 1.1) +
  geom_hline(yintercept = 0) +
  scale_color_brewer(palette = "Set1") +
  scale_linetype_manual(values = c(rep(1, 4), 5)) +
  theme_bw() + 
  labs(x = "Year", y = "GtCO2", color = "Model", linetype = "Model",
       caption = "Data source: GCB 2024",
       title = "GCB: Annual variation in global Eluc (vs lag (Eluc))")

  • The GCB estimate is the simple average of the four models. Adding LUCE raised the overall ELUC estimate, as the relative weight of HC2023—the model with the lowest emissions—was reduced. However, the mean still remains lower than the estimates from the other three models in most years.

  • HC 2023 is systematically lower than others, but annual variations are fairly consistent across models.

Code
Eluc_world %>% 
  gather(element, value, -year, -model) %>% 
  filter(element != "Net", model != "GCB") %>% 
  mutate(value = value * 44 / 12) %>% 
  ggplot() +
  geom_bar(aes(x = year, y = value, fill = element), 
           color = "black", size = 0.1, stat = "identity") +
  facet_wrap(~model) +
  geom_hline(yintercept = 0) +
  scale_fill_brewer(palette = "Set2", guide = guide_legend(nrow = 2)) +
  theme_bw() + theme(legend.position = "top") +
  labs(x = "Year", y = "GtCO2", fill = "Element", 
       caption = "Data source: GCB 2024",
       title = "GCB: Global Eluc by element") 

  • If looking into the decomposition by component, estimates for wood harvest & other forest management even differ in sign.

  • All models used the same peat drainage & fires.

  • Interestingly, BLUE, OSCAR, and LUCE produce closer overall results (compared to HC2023)—but for different underlying reasons!

Code
Ele <- c("deforestation (total)",
"forest regrowth (total)",
"other transitions",
"wood harvest & other forest management",
"peat drainage & peat fires",
"Net")

Ele_lab <- c("deforestation", "forest regrowth",
"other transitions",
"wood harvest & other forest management",
"peat drainage & fires",
"Net total")
Eluc_world %>% 
  filter(year >= 1959) %>% 
  group_by(model) %>% 
  summarise(across(Net:`wood harvest & other forest management`,
                   ~round(sum(.x, na.rm = TRUE) * 44 / 12, 0))) %>% 
  gather(element, value, -model) %>% 
  spread(model, value) %>% 
  mutate(element = factor(element, levels = Ele, labels = Ele_lab)) %>% 
  arrange(element) %>% 
  kbl(format = 'html',
      caption = "Table: Cumulative Eluc emissions by component and model (1959–2023, in GtCO₂)") %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
Table: Cumulative Eluc emissions by component and model (1959–2023, in GtCO2)
element BLUE HC2023 OSCAR LUCE GCB
deforestation 461 356 469 377 416
forest regrowth -278 -195 -251 -205 -232
other transitions 42 29 52 30 38
wood harvest & other forest management 145 -33 26 134 68
peat drainage & fires 52 52 52 52 52
Net total 422 209 350 389 342
  • Cumulatively, diff could be very large (see the table below)!

GCAM vs. GCB for Eluc

The decomposition of ELUC components is provided by the GCB only at the global level, not in the country-level data. This detailed breakdown is valuable, especially since GCAM does not include peat-related emissions—an element that is accounted for separately in the GCB models. Here, we first compare global results and examine the role of peat-related emissions in the overall accounting. We then move on to compare regional results based on net total emissions. Obtaining more detailed, component-level data alongside country-level land-use change estimates in the future would be helpful for further analysis.

Global comparison

Code
readr::read_csv("../data/GCB/GCAM_BYU_LULUCF.csv", comment = "#") ->
  GCAM_Eluc

GCAM_Eluc %>% 
  gcamdata::gather_years() %>% 
  transmute(region, year, value = value * 44/12/1000) ->
  GCAM_Eluc

GCAM_Eluc %>% 
  group_by(year) %>% filter(year <= 2025) %>% 
  summarize(value = sum(value), .groups = "drop") %>% 
  mutate(model = "GCAM") %>% 
  bind_rows(
    Eluc_world %>% 
      transmute(year, model, value = Net * 44 / 12)
  ) -> df

df %>% mutate(model = factor(model,
              levels = c("BLUE", "HC2023", "OSCAR", "LUCE", "GCB", "GCAM"))) ->
  df

df %>% filter(!(model == "GCAM" & !year %in% c(seq(1980, 2015, 5), 2021, 2025))) %>% 
  ggplot() +
  geom_vline(xintercept = 2021, size = 0.5) +
  geom_vline(xintercept = 2015, size = 0.5) +
  geom_line(aes(x = year, y = value, color = model, linetype = model),
            size = .7) +
  geom_point(data = df %>% filter(model == "GCAM"),
             aes(x = year, y = value), color = "black", size = 0.6) +
    geom_point(data = df %>% 
                 filter(model == "GCAM", 
                        year %in% c(seq(1980, 2015, 5), 2021, 2025)),
             aes(x = year, y = value), color = "red", size = 1, shape = 21) +
  geom_hline(yintercept = 0) +
  scale_color_brewer(palette = "Set1") +
  scale_linetype_manual(values = c(rep(1, 4), 5, 1)) +
  theme_bw() + 
  labs(x = "Year", y = "GtCO2", color = "Model", linetype = "Model",
       caption = "Data source: GCB 2024",
       title = "GCB: Global Eluc + GCAM",
       subtitle = "GCAM base years in red dots") -> p

ggplotly(p)
  • GCAM can report annual ELUC (black dots), but it is not clear about the methods there. We usually use linear interpolation though (yellow line).

  • The GCAM results presented here are very preliminary (1st round under review). There are known issues that have not yet been resolved—for example, the large area of managed forest conversion in recent years, which stems from assumptions about carbon density (a long and complex story).

  • GCAM results appear at the low end compared to GCB models (except for 2015). It is important to note that GCAM does not (explicitly) include peat related emissions.

Regional comparison

We first create a region mapping between GCB countries and GCAM 32 regions to aggregate GCB data into GCAM regions.

Load regional data

Code
readr::read_csv("../data/common/iso_GCAM_regID.csv", comment = "#") -> iso_GCAM_regID
readr::read_csv("../data/common/GCAM_region_names.csv", comment = "#") -> GCAM_region_names
readr::read_csv("../data/GCB/Mapping_GCB_GCAM_Reg.csv", comment = "#") -> Mapping_GCB_GCAM_Reg

lapply(c("BLUE", "OSCAR", "H&C2023", "LUCE"), 
       function(BKM){

  readxl::read_excel("../data/GCB/National_LandUseChange_Carbon_Emissions_2024v1.01.xlsx", sheet = BKM, skip = 7) %>% rename(year = `unit: Tg C/year`) %>% 
  gather(GCB_reg, value, -year) %>% mutate(BKM = BKM)
}) %>% bind_rows() -> GCB_BKM_Eluc


GCB_BKM_Eluc %>% filter(year >= 1980) %>% 
  left_join(Mapping_GCB_GCAM_Reg, by = "GCB_reg") %>% 
  mutate(iso = if_else(GCB_reg == "Côte d'Ivoire", "civ", iso),
         iso = if_else(GCB_reg == "Türkiye", "tur", iso)) %>%
  filter(!is.na(iso)) %>% 
  left_join(iso_GCAM_regID %>% select(iso, GCAM_region_ID), by = "iso") %>% 
  left_join(GCAM_region_names, by = "GCAM_region_ID") %>% 
  group_by(region, model = BKM, year) %>% 
  summarize(value = sum(value) * 44/12/1000, .groups = "drop") ->
  GCB_BKM_Eluc_agg



GCAM_Eluc %>% 
  filter(year <= 2025) %>% 
  mutate(model = "GCAM") %>% 
  bind_rows(
    GCB_BKM_Eluc_agg 
  ) -> df

df %>% filter(region %in% c("China", "Brazil", "Africa_Western", 
                            "Indonesia")) -> 
  df1

df1 %>% 
  filter(!(model == "GCAM" & !year %in% c(seq(1980, 2015, 5), 2021, 2025))) %>% 
  mutate(model = factor(model,
                        levels = c("BLUE", "H&C2023", "OSCAR", "LUCE", "GCB", "GCAM"),
                        labels = c("BLUE", "HC2023", "OSCAR", "LUCE", "GCB","GCAM"))) %>% 
  ggplot() + facet_wrap(~region, scale = "free", ncol = 1) +
  geom_vline(xintercept = 2021, size = 0.5) +
  geom_vline(xintercept = 2015, size = 0.5) +
  geom_line(aes(x = year, y = value, color = model, linetype = model),
            size = .7) +
  geom_point(data = df1 %>% 
               filter(model == "GCAM", 
                      year %in% c(seq(1980, 2015, 5), 2021, 2025)),
             aes(x = year, y = value), color = "red", fill = "black", size = 1, shape = 21) +
  geom_hline(yintercept = 0) +
  scale_color_brewer(palette = "Set1") +
  scale_linetype_manual(values = c(rep(1, 4), 5, 1)) +
  theme_bw() + 
  labs(x = "Year", y = "GtCO2", color = "Model", linetype = "Model",
       caption = "Data source: GCB 2024",
       title = "GCB: region Eluc vs. GCAM",
       subtitle = "GCAM base years in red dots") -> p
ggplotly(p)
Code
df %>% filter(region %in% c("USA", "China", "Brazil", "Africa_Western", 
                            "Indonesia", "Southeast Asia", "EU-12", "EU-15")) -> 
  df1

df1 %>% 
  filter(!(model == "GCAM" & !year %in% c(seq(1980, 2015, 5), 2021, 2025))) %>% 
  mutate(model = factor(model,
                        levels = c("BLUE", "H&C2023", "OSCAR", "LUCE","GCB", "GCAM"),
                        labels = c("BLUE", "HC2023", "OSCAR", "LUCE", "GCB","GCAM"))) %>% 
  ggplot() + facet_wrap(~region, scale = "free_y", ncol = 2) +
  geom_vline(xintercept = 2021, size = 0.5) +
  geom_vline(xintercept = 2015, size = 0.5) +
  geom_line(aes(x = year, y = value, color = model, linetype = model),
            size = .7) +
  geom_point(data = df1 %>% 
               filter(model == "GCAM", 
                      year %in% c(seq(1980, 2015, 5), 2021, 2025)),
             aes(x = year, y = value), color = "red", fill = "black", size = 1, shape = 21) +
  geom_hline(yintercept = 0) +
  scale_color_brewer(palette = "Set1") +
  scale_linetype_manual(values = c(rep(1, 4), 5, 1)) +
  theme_bw() + 
  labs(x = "Year", y = "GtCO2", color = "Model", linetype = "Model",
       caption = "Data source: GCB 2024",
       title = "GCB: region Eluc vs. GCAM",
       subtitle = "GCAM base years in red dots")

Code
df %>% filter(!region %in% c("USA", "China", "Brazil", "Africa_Western", 
                            "Indonesia", "Southeast Asia", "EU-12", "EU-15")) -> 
  df1

df1 %>% 
  filter(!(model == "GCAM" & !year %in% c(seq(1980, 2015, 5), 2021, 2025))) %>% 
  mutate(model = factor(model,
                        levels = c("BLUE", "H&C2023", "OSCAR", "LUCE","GCB", "GCAM"),
                        labels = c("BLUE", "HC2023", "OSCAR", "LUCE", "GCB","GCAM"))) %>% 
  ggplot() + facet_wrap(~region, scale = "free_y", ncol = 4) +
  geom_vline(xintercept = 2021, size = 0.5) +
  geom_vline(xintercept = 2015, size = 0.5) +
  geom_line(aes(x = year, y = value, color = model, linetype = model),
            size = .7) +
  geom_point(data = df1 %>% 
               filter(model == "GCAM", 
                      year %in% c(seq(1980, 2015, 5), 2021, 2025)),
             aes(x = year, y = value), color = "red", fill = "black", size = 1, shape = 21) +
  geom_hline(yintercept = 0) +
  scale_color_brewer(palette = "Set1") +
  scale_linetype_manual(values = c(rep(1, 4), 5, 1)) +
  theme_bw() + 
  labs(x = "Year", y = "GtCO2", color = "Model", linetype = "Model",
       caption = "Data source: GCB 2024",
       title = "GCB: region Eluc vs. GCAM",
       subtitle = "GCAM base years in red dots")

Footnotes

  1. Xin interacted with the lead author Zhangcai Qin ~10 years ago when he was in Argonne and contributed to the CCLUB model. Min Chen is also a coauthor in their recent paper↩︎

  2. GCAM used Houghton (~2000) data previously, but have recently changed to SoilGrid and other sources.↩︎

  3. Matthew Gidden, now with JGCRI, is an expert.↩︎